import os
import Image
import pprint
import JpegImagePlugin
import TgaImagePlugin
import PngImagePlugin
import GifImagePlugin
Image._initialized = 1

from BeautifulSoup import BeautifulStoneSoup

from mp4 import M4ATags
import EXIF
import struct
import types
import datetime
from django.utils.encoding import smart_str
from iptcinfo import IPTCInfo
from mp3 import get_mp3tag, mp3info


#from PIL import Image    
from PIL.ExifTags import TAGS

HAS_HACHOIR_INSTALLED = False
try:
    from hachoir_core.error import HachoirError
    from hachoir_core.cmd_line import unicodeFilename
    from hachoir_parser import createParser
    from hachoir_core.tools import makePrintable
    from hachoir_metadata import extractMetadata
    from hachoir_core.i18n import getTerminalCharset
    HAS_HACHOIR_INSTALLED = True
except ImportError, e:
    print "Importerrr", e


def getXMPImageMetadata(filename, debug = False):
    """

    """
    result = {'xmp_title': '', 'xmp_description': '', 'xmp_tags': []}

    soup = None
    if 1:#try:
        #print filename,
        try:
            im = Image.open(filename)
            rdf = im.app['APP1']   # APP1 as described by Adobe for XMP
            soup = BeautifulStoneSoup(rdf)
        except Exception, e:
            pass
            #print "Skipping XMP-extraction for", filename, "because of", e
            #print "FAILED"
            #return result
        #else:
        #    print

        if not soup:
            return

        tags = []
        s = soup.find('dc:subject')
        if s:
            for e in s.findAll('rdf:li'):
                tags.append(str(e.string))
        for tag in tags:
            if not tag in result['xmp_tags']:
                try:
                    result['xmp_tags'].append(unicode(tag.lower(), 'ISO-8859-1'))
                except (UnicodeEncodeError, UnicodeDecodeError), e:
                    print "Skipping tag for file", filename
        desc = soup.find('dc:description')
        album = soup.find('rdf:description')
        try:
            if album['album:notes']:
                result['xmp_description'] = album['album:notes'].encode('utf-8')
        except Exception, e:
            pass #print "skipping xmp-description", e, filename
        #result['description'] = desc.find('rdf:li').string
        
        if desc:
            result['xmp_title'] = desc.find('rdf:li').string.encode('utf-8')
##    except (KeyError, AttributeError, OverflowError, IOError), e:
##        if debug:
##            print 'No XMP info found in %s. Exception : %s' %  (filename, e)
##    except UnicodeEncodeError, e:
##        if soup: print soup
##        print "Error in ", filename, e

    return result

    #print "notes", album['album:notes']
    #print "caption", desc.find('rdf:li').string
    #print soup.prettify()

    ##
    ##xml = '''
    ##<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="3.1.2-113">
    ## <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
    ##  <rdf:Description rdf:about=""
    ##    xmlns:tiff="http://ns.adobe.com/tiff/1.0/"
    ##    xmlns:xap="http://ns.adobe.com/xap/1.0/"
    ##    xmlns:exif="http://ns.adobe.com/exif/1.0/"
    ##    xmlns:dc="http://purl.org/dc/elements/1.1/"
    ##    xmlns:album="http://ns.adobe.com/album/1.0/"
    ##   tiff:Orientation="6"
    ##   tiff:YCbCrPositioning="2"
    ##   tiff:XResolution="300/1"
    ##   tiff:YResolution="300/1"
    ##   tiff:ResolutionUnit="2"
    ##   tiff:Make="NIKON CORPORATION"
    ##   tiff:Model="NIKON D70"
    ##   tiff:NativeDigest="256,257,258,259,262,274,277,284,530,531,282,283,296,301,318,319,529,532,306,270,271,272,305,315,33432;490694D97AB8E6EACF02EB60A166F4E0"
    ##   xap:ModifyDate="2006-09-20T23:59:24+02:00"
    ##   xap:CreatorTool="Adobe Photoshop Elements 4.0"
    ##   exif:ExifVersion="0.2.2.1"
    ##   exif:FlashpixVersion="0.1.0.0"
    ##   exif:ColorSpace="1"
    ##   exif:CompressedBitsPerPixel="1/1"
    ##   exif:PixelXDimension="3008"
    ##   exif:PixelYDimension="2000"
    ##   exif:DateTimeOriginal="2004-12-26T14:36:31.9+01:00"
    ##   exif:DateTimeDigitized="2004-12-26T14:36:31.9+01:00"
    ##   exif:ExposureTime="10/130"
    ##   exif:FNumber="45/10"
    ##   exif:ExposureProgram="2"
    ##   exif:ExposureBiasValue="0/6"
    ##   exif:MaxApertureValue="43/10"
    ##   exif:MeteringMode="5"
    ##   exif:LightSource="0"
    ##   exif:FocalLength="460/10"
    ##   exif:SensingMethod="2"
    ##   exif:FileSource="3"
    ##   exif:SceneType="1"
    ##   exif:CustomRendered="0"
    ##   exif:ExposureMode="0"
    ##   exif:WhiteBalance="0"
    ##   exif:DigitalZoomRatio="1/1"
    ##   exif:FocalLengthIn35mmFilm="69"
    ##   exif:SceneCaptureType="0"
    ##   exif:GainControl="0"
    ##   exif:Contrast="0"
    ##   exif:Saturation="0"
    ##   exif:Sharpness="0"
    ##   exif:SubjectDistanceRange="0"
    ##   exif:NativeDigest="36864,40960,40961,37121,37122,40962,40963,37510,40964,36867,36868,33434,33437,34850,34852,34855,34856,37377,37378,37379,37380,37381,37382,37383,37384,37385,37386,37396,41483,41484,41486,41487,41488,41492,41493,41495,41728,41729,41730,41985,41986,41987,41988,41989,41990,41991,41992,41993,41994,41995,41996,42016,0,2,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,20,22,23,24,25,26,27,28,30;BABA4B5DDBFF02E6EFD7088E1BE19E14"
    ##   album:Notes="her er noen notes">
    ##   <exif:ComponentsConfiguration>
    ##    <rdf:Seq>
    ##     <rdf:li>1</rdf:li>
    ##     <rdf:li>2</rdf:li>
    ##     <rdf:li>3</rdf:li>
    ##     <rdf:li>0</rdf:li>
    ##    </rdf:Seq>
    ##   </exif:ComponentsConfiguration>
    ##   <exif:Flash
    ##    exif:Fired="False"
    ##    exif:Return="0"
    ##    exif:Mode="0"
    ##    exif:Function="False"
    ##    exif:RedEyeMode="False"/>
    ##   <exif:CFAPattern>
    ##    <rdf:Description
    ##     exif:Columns="2"
    ##     exif:Rows="2">
    ##    <exif:Values>
    ##     <rdf:Seq>
    ##      <rdf:li>2</rdf:li>
    ##      <rdf:li>1</rdf:li>
    ##      <rdf:li>1</rdf:li>
    ##      <rdf:li>0</rdf:li>
    ##     </rdf:Seq>
    ##    </exif:Values>
    ##    </rdf:Description>
    ##   </exif:CFAPattern>
    ##   <dc:subject>
    ##    <rdf:Bag>
    ##     <rdf:li>Tuva</rdf:li>
    ##    </rdf:Bag>
    ##   </dc:subject>
    ##   <dc:description>
    ##    <rdf:Alt>
    ##     <rdf:li xml:lang="x-default">Tuva drikker av et glass</rdf:li>
    ##    </rdf:Alt>
    ##   </dc:description>
    ##  </rdf:Description>
    ## </rdf:RDF>
    ##</x:xmpmeta>
    ##'''
    
def getAviInfo(AVIFile):
    """
    Provides information about an AVI/DivX movie/file

    Thanks to   : Robert Voje for providing Pascal-source and guidance to hex and bits

    The information about the AVI-fileformat was taken from MSDN.microsoft.com
    This piece of code has been tested in Win2k and was developed using ActivePython version 2.1.1.

    TODO :

     - calculate datarate and more usefull values
     - do some more testing
     """
    frmt = '4sl4s4sl4s4slllllllllllllll4sl4s4sl4s4sllHHlllllllHHHH'
    RecordSize = struct.calcsize(frmt)
    RiffIdent,\
    ChunkSize,\
    AVIIdent,\
    ListIdent1,\
    ListChunkSize1,\
    hdrlHeader,\
    AVIHeaderIdent,\
    AVIChunkSize,\
    avihMicroSecPerFrame,\
    avihMaxBytesPerSec,\
    avihPaddingGranularity,\
    avihFlags,\
    avihTotalFrames,\
    avihInitialFrames,\
    avihStreams,\
    avihSuggestedBufferSize, \
    avihWidth,\
    avihHeight,\
    avihScale,\
    avihRate,\
    avihStart,\
    avihLength,\
    ListIdent2,\
    ListChunkSize2,\
    strlHeader,\
    strlIdent,\
    strlChunkSize,\
    strlfccType,\
    strlfccHandler,\
    strlFlags,\
    strlCaps,\
    strlPriority,\
    strlLanguage,\
    strlScale,\
    strlRate,\
    strlStart,\
    strlLength,\
    strlSuggestedBufferSize, \
    strlQuality,\
    strlSampleSize,\
    strlFrameLeft,\
    strlFrameTop,\
    strlFrameRight,\
    strlFrameBottom = struct.unpack(frmt, open(AVIFile).read(RecordSize))

    # Calculate some usfull values
    try:
        FrameRate = float(strlRate) / float(strlScale)
    except:
        FrameRate = 0
    try:
        RunningTimeInSecs = float(strlLength) / (float(strlRate)/float(strlScale))
    except:
        RunningTimeInSecs = 0
    try:
        mins = int(RunningTimeInSecs / 60)
        secs = int(RunningTimeInSecs - (mins*60))
        hours = int(mins / 60)
        mins = mins - (hours*60)
        if len(str(mins)) == 1:
            RunningTime = '%s:0%s:%s' % (hours, mins, secs)
        else:
            RunningTime = '%s:%s:%s' % (hours, mins, secs)
    except:
        mins = 0
        secs = 0
        hours = 0
        RunningTime = 'N/A'

    SizeInBytes = os.stat(AVIFile)[6]
    SizeInMBytes = (SizeInBytes / 1024) / 1024
    
    # Return a dictionary. Perhaps just return usefull values ???
    return locals()

def getWmaInfo(filename):
    import wmainfo
    foo = wmainfo.WmaInfo(filename)
    result = {}
    for k,v in foo.tags.items():
        result[k] = v
    for k,v in foo.info.items():
        result[k] = v
    result['DRM'] = foo.drm and 'Present' or 'None'
    return result
# self.drm          :: '1' if DRM present else 'None'
# self.tags         :: dict of strings (id3 like data)
# self.info         :: dict of variable types (non-id3 like data)
# self.headerObject :: dict of arrays (name, GUID, size and offset of ASF objects)

  # Public methods:

# boolean hasdrm()             :: returns True if file has DRM
# boolean hastag(string 'str') :: returns True if self.tags['str'] exists
# None printtags()             :: pretty-print self.tags dict
# boolean hastag(string 'str') :: returns True if self.info['str'] exists
# None printinfo()             :: pretty-print self.info dict
# None parsestream()           :: parse Asf_Stream_Property_Object
   # ... which will create another public attribute:
# self.stream       :: dict of variable types (stream properties data)
# For more/different documentation see badcomputer.org/unix/code/wmainfo/

metering_modes = {
    '1': 'Average',
    '2': 'CenterWeightedAverage',
    '3': 'Spot',
    '4': 'Multispot',
    '5': 'Pattern',
    '6': 'Partial'
    }

light_sources = {
    0: 'unknown',
    1: 'Daylight',
    2: 'Fluoroscent',
    3: 'Tungsten',
    17: 'Standard light A',
    18: 'Standard light B',
    19: 'Standard light C',
    20: 'D55',
    21: 'D65',
    22: 'D75'
    }

def exifParser(img_file):
    fname, fext = os.path.splitext(img_file)
    if fext.lower() == '.crw' and os.path.exists(fname + '.thm'):
        img_file = fname + '.thm'

    result = {'exif': {}, 'pil': {}}
    
    try:
        i = Image.open(img_file)
        info = i._getexif()
        if info:
            for tag_id, tag_name in TAGS.items():
                if tag_name == 'MakerNote' or tag_name == 'UserComment':
                    continue
                result['pil'][tag_name] = info.get(tag_id, None)
    except Exception, e:
        print "Error extracting EXIF using PIL from %s because %s." % (img_file, e)


#data = exif.process_file(file)
#		if not data:
#			return "No EXIF Information available."
#		x = data.keys()
#		x.sort()
#		count = 0
#		vals = [0,4,9,11,12,15,17,18,22,25,38,39,41,43,45]
#		"""
#		0  - ApertureValue
#		4  - DateTimeDigitized
#		9  - ExposureBiasValue
#		11 - ExposureProgram
#		12 - ExposureTime
#		15 - Flash
#		17 - FocalLength
#		18 - ISOSpeedRatings
#		22 - MeteringMode
#		25 - ShutterSpeedValue
#		38 - Image Make
#		39 - Image Model
#		41 - Image ResolutionUnit
#		43 - Image XResolution
#		45 - Image YResolution
#		"""
#		retVal = []
#		for i in x:

    try:
        EXIF_DATA = EXIF.process_file(open(img_file, 'rb'))
        for k,v in EXIF_DATA.items():
            
            if 'EXIF Tag' in k or 'MakerNote Tag' in k or 'Image Tag' in k:
                continue
            
            if type(EXIF_DATA.get(k, None)) == types.ListType or k == 'JPEGThumbnail':
                continue
            
            if hasattr(v, 'printable'):
                EXIF_DATA[k] = v.printable
            else:
                EXIF_DATA[k] = v
                
            val = EXIF_DATA.get(k, None)
            if len(val) > 100:  
                continue
    
            result['exif'][k] = val
    
        if EXIF_DATA:
            date = EXIF_DATA.get('EXIF DateTimeDigitized', None)
            if date:
                datepart, timepart = date.split(' ')
                year, month, day = datepart.split(':')
                hour, minute, second = timepart.split(':')
                year, month, day, hour, minute, second = map(int, [year, month, day, hour, minute, second])
                result['DateTime'] = datetime.datetime(year, month, day, hour, minute, second)
        else:
            result['DateTime'] = datetime.datetime.fromtimestamp(os.stat(img_file).st_ctime)
    except Exception, e:
        print "Error extracting EXIF from %s because of %s." % (img_file, e)
 
    iptc_info = {}
    try:
        inf = IPTCInfo(img_file)
    except:
        pass
    else:
        if len(inf.data) > 4:
            for k,v in inf.data.items():
                if inf.data.keyAsStr(k).lower().find('nonstandard') == 0:
                    continue
                
                if type(v) == types.UnicodeType:
                    iptc_info[inf.data.keyAsStr(k)] = v.encode('utf-8')
                elif type(v) == types.ListType:
                    iptc_info[inf.data.keyAsStr(k)] = [t.encode('utf-8') for t in inf.data.get('keywords', [])]
            result['iptc'] = iptc_info
    
    res = {'DateTime': result['DateTime']}
    # exif
    if result.get('exif'):
        #res['Height'] = int(result['exif']['EXIF ExifImageLength'])
        #res['Width'] = int(result['exif']['EXIF ExifImageWidth'])
        res['ExposureProgram'] = result['exif'].get('EXIF ExposureProgram')
        res['ExposureTime'] = result['exif'].get('EXIF ExposureTime')
        res['Flash'] = result['exif'].get('EXIF Flash') == 'Yes' and True or False
        res['FocalLength'] = result['exif'].get('EXIF FocalLength', 0)
        res['SceneType'] = result['exif'].get('EXIF SceneType')
        res['CameraMake'] = result['exif'].get('Image Make')
        res['CameraModel'] = result['exif'].get('Image Model')
    #iptc
    if result.get('iptc'):
        res['by-line'] = result['iptc'].get('by-line')
        res['by-line title'] = result['iptc'].get('by-line title')
        res['caption/abstract'] = result['iptc'].get('caption/abstract')
        res['category'] = result['iptc'].get('category')
        res['city'] = result['iptc'].get('city')
        res['contact'] = result['iptc'].get('contact')
        res['copyright notice'] = result['iptc'].get('copyright notice')
        res['country/primary location name'] = result['iptc'].get('country/primary location name')
        res['credit'] = result['iptc'].get('credit')
        res['headline'] = result['iptc'].get('headline')
        res['keywords'] = result['iptc'].get('keywords')
        res['object name'] = result['iptc'].get('object name')
        res['province/state'] = result['iptc'].get('province/state')
        res['source'] = result['iptc'].get('source')
        res['special instructions'] = result['iptc'].get('special instructions')
        res['supplemental category'] = result['iptc'].get('supplemental category')
        res['writer/editor'] = result['iptc'].get('writer/editor')
    # pil
    if result.get('pil'):
        res['FNumber'] = result['pil'].get('FNumber')[0] / result['pil'].get('FNumber')[1]
        res['ISOSpeedRatings'] = result['pil'].get('ISOSpeedRatings')
        res['Orientation'] = result['pil'].get('Orientation')
        res['Height'] = int(result['pil'].get('ExifImageHeight'))
        res['Width'] = int(result['pil'].get('ExifImageWidth'))

    xmp = getXMPImageMetadata(img_file)
    if xmp:
        result.update(xmp)
        res['title'] = xmp.get('xmp_title')
        res['description'] = xmp.get('xmp_description')
        res['keywords'] = res.get('keywords', []).extend(xmp.get('xmp_tags', []))
    
    return result

def multiExtractor(filename):
    """
    
    """
    if not HAS_HACHOIR_INSTALLED:
        return {}
        
    filename, realname = unicodeFilename(filename), filename
    parser = createParser(filename, realname)
    if not parser:
        return {}
    try:
        metadata = extractMetadata(parser)
    except HachoirError, err:
        return {}
    except:
        return {}

    if  not metadata:
        return {}

    result = {}
    for item in metadata:
        if item.values:
            result[item.description] = ','.join([unicode(dataitem.text) for dataitem in item.values])
    return result

def mp3Handler(filename):
    result = {}    
    i1 = mp3info(filename)
    for k,v in i1.items():
      result[k] = v
      
    i2 = get_mp3tag(filename)
    for k,v in i2.items():
      result[k] = v

    return result

def mp4Handler(filename):
    return M4ATags(filename)

extensions_mapping = {
    'mp3': mp3Handler,
    'avi': getAviInfo,
    'jpg': exifParser,
    'nef': exifParser,
    'png': exifParser,
    'tiff': exifParser,
    'gif': exifParser,
    'jpeg': exifParser,
    'crw': exifParser,
    'cr2': exifParser,
    'bmp': exifParser,
    'mpeg': None,
    'mpg': None,
    'mov': None,
    'mkv': None,
    'mp4': None,
    'm4a': mp4Handler,
    'm4v': None,
    'ogg': None,
    'flac': None,
    'doc': None,
    'py': None,
    'wma': getWmaInfo,
    'wmv': getWmaInfo    
}

def getMetadata(filename):
    """
    
    """
    fname, fext = os.path.splitext(filename)
    fext = fext[1:].lower()
    if fext in ('jpg', 'nef'):
        result = {}
    else:
        result = {
            'generic extractor': multiExtractor(filename)
            }
    f = extensions_mapping.get(fext, None)
    if f:
        d = {}
        try:
            d = f(filename)
        except Exception, e:
            #print filename
            #print e
            pass
        if d:
            for k,v in d.items():
                #if v != None:
                result[k] = v#unicode(v).strip()
    return result